export namespace Utils {
    export function clamp(value: number, min: number, max: number) {
        return min > max ? value : Math.min(Math.max(value, min), max);
    }

    export function angle2Radian(angle: number): number {
        return angle / 180 * Math.PI;
    }

    export function radian2Angle(radian: number): number {
        return radian / Math.PI * 180;
    }

    let _cacheRT: cc.RenderTexture = null;
    let _cacheTexture1: cc.Texture2D = null;
    let _cacheTexture2: cc.Texture2D = null;
    let _cacheCamera: cc.Camera = null;
    let _cacheCameraNode: cc.Node = null;
    let _cacheSprite: cc.Sprite = null;
    let _cacheSpriteNode: cc.Node = null;

    function getRenderTexture(
        width: number,
        height: number,
        pixelFormat: cc.RenderTexture.DepthStencilFormat,
        useCache: boolean = true
    ) {
        let rt: cc.RenderTexture = null;
        if (useCache) {
            if (!this._cacheRT) {
                this._cacheRT = new cc.RenderTexture();
            }
            rt = this._cacheRT;
        } else {
            rt = new cc.RenderTexture();
            rt.setFlipY(true);
        }
        rt.initWithSize(width, height, pixelFormat);
        return rt;
    }

    function checkCacheCamera() {
        if (!_cacheCameraNode) {
            _cacheCameraNode = new cc.Node();
            _cacheCamera = _cacheCameraNode.addComponent(cc.Camera);
        }
    }

    function checkCacheSprite() {
        if (!_cacheSpriteNode) {
            _cacheSpriteNode = new cc.Node();
            _cacheSprite = _cacheSpriteNode.addComponent(cc.Sprite);
            _cacheSprite.spriteFrame = new cc.SpriteFrame();
        }
    }

    function checkCacheTexture() {
        _cacheTexture1 = _cacheTexture1 || new cc.Texture2D();
        _cacheTexture2 = _cacheTexture2 || new cc.Texture2D();
    }

    /**
     * 截屏
     * @returns 
     */
    export function screenshot(outTex?: cc.RenderTexture) {
        let cameras = Array.from(cc.Camera.cameras);
        cameras.sort((a, b) => {
            return a.depth - b.depth;
        });

        let rt = outTex || getRenderTexture(cc.winSize.width, cc.winSize.height,
            cc.RenderTexture.DepthStencilFormat.RB_FMT_D24S8, false);
        let scene = cc.director.getScene();
        for (let i = 0, len = cameras.length; i < len; i++) {
            if (cameras[i].targetTexture) {
                cameras[i].render(scene);
            } else {

                cameras[i].targetTexture = rt;
                cameras[i].render(scene);
                cameras[i].targetTexture = null;
            }
        }
        return rt;
    }

    /**
     * 将节点渲染到纹理中
     * @param node 
     * @param desTexture 
     * @param downlevel 
     * @returns 
     */
    export function renderNodeToTexture(
        node: cc.Node,
        desTexture?: cc.Texture2D,
        downlevel: number = 1
    ) {
        desTexture = desTexture || new cc.Texture2D();
        desTexture.setFlipY(true);

        let width = Math.floor(cc.winSize.width / downlevel);
        let height = Math.floor(cc.winSize.height / downlevel);
        let rt = getRenderTexture(width, height, cc.RenderTexture.DepthStencilFormat.RB_FMT_D24S8);

        checkCacheCamera();

        _cacheCameraNode.parent = node;
        _cacheCamera.targetTexture = rt;
        _cacheCamera.render(node);
        _cacheCamera.targetTexture = null;
        _cacheCameraNode.parent = null;

        let localBox = node.getBoundingBox();
        let x = Math.ceil(clamp(localBox.x - node.x + cc.winSize.width * 0.5, 0, cc.winSize.width) / downlevel);
        let y = Math.ceil(clamp(localBox.y - node.y + cc.winSize.height * 0.5, 0, cc.winSize.height) / downlevel);
        let w = Math.floor(clamp(localBox.width, 0, cc.winSize.width) / downlevel);
        let h = Math.floor(clamp(localBox.height, 0, cc.winSize.height) / downlevel);

        desTexture.initWithData(rt.readPixels(null, x, y, w, h),
            rt.getPixelFormat(), w, h);
        return desTexture;
    }

    /**
     * 将纹理渲染到纹理中
     * @param srcTexture 
     * @param desTexture 
     * @param materials 
     * @param iteration 
     * @param downlevel 
     */
    export function renderTextureToTexture(
        srcTexture: cc.Texture2D,
        desTexture: cc.Texture2D,
        materials: cc.Material | Array<cc.Material>,
        iteration: number = 1,
        downlevel: number = 1
    ) {
        if (materials instanceof cc.Material) {
            materials = [materials];
        }

        checkCacheSprite();
        checkCacheTexture();

        let currScene = cc.director.getScene();
        let renderCount = 0;
        let maxRenderCount = iteration * materials.length;
        let tempTex;
        _cacheSpriteNode.parent = currScene;
        for (let i = 0; i < iteration; i++) {
            for (let i = 0, len = materials.length; i < len; i++) {
                renderCount++;
                _cacheSprite.setMaterial(0, materials[i]);
                _cacheSprite.spriteFrame.setTexture(renderCount === 1 ? srcTexture : _cacheTexture1);
                _cacheSprite["_updateMaterial"]();

                renderNodeToTexture(
                    _cacheSpriteNode,
                    renderCount === maxRenderCount ? desTexture : _cacheTexture2,
                    renderCount === 1 ? downlevel : 1);

                //交换纹理
                if (renderCount < maxRenderCount) {
                    tempTex = _cacheTexture2;
                    _cacheTexture2 = _cacheTexture1;
                    _cacheTexture1 = tempTex;
                }
            }
        }
        _cacheSpriteNode.parent = null;
        _cacheSprite.spriteFrame.setTexture(null);
        desTexture.setFlipY(!!(renderCount % 2));
    }

    export function save(fileName, data) {
        if (!(data instanceof Array)) {
            data = [data];
        }
        var blob = new Blob(data);
        var URL = window.URL || window.webkitURL;
        var bloburl = URL.createObjectURL(blob);
        var anchor = document.createElement("a");
        if ('download' in anchor) {
            anchor.style.visibility = "hidden";
            anchor.href = bloburl;
            anchor.download = fileName;
            document.body.appendChild(anchor);
            var evt = document.createEvent("MouseEvents");
            evt.initEvent("click", true, true);
            anchor.dispatchEvent(evt);
            document.body.removeChild(anchor);
            window.URL.revokeObjectURL(anchor.href);
        }
    }
}